package cn.springcloud.fix.common.core.utils;

import cn.springcloud.fix.common.core.utils.support.BaseBeanCopier;
import cn.springcloud.fix.common.core.utils.support.BeanProperty;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.cglib.beans.BeanGenerator;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.cglib.core.CodeGenerationException;
import org.springframework.util.Assert;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 实体工具类
 */
@Slf4j
public class BeanUtil extends BeanUtils {

    /**
     * 实例化对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T newInstance(Class<?> clazz) {
        return (T) instantiateClass(clazz);
    }

    /**
     * 实例化对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T newInstance2(Class<?> clazz) {
        try {
            return (T) clazz.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 实例化对象
     */
    public static <T> T newInstance(String clazzStr) {
        try {
            Class<?> clazz = Class.forName(clazzStr);
            return newInstance(clazz);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取Bean的属性
     */
    public static Object getProperty(Object bean, String propertyName) {
        Assert.notNull(bean, "bean Could not null");
        return BeanMap.create(bean).get(propertyName);
    }

    /**
     * 设置Bean属性
     */
    public static void setProperty(Object bean, String propertyName, Object value) {
        Assert.notNull(bean, "bean Could not null");
        BeanMap.create(bean).put(propertyName, value);
    }

    /**
     * 深复制
     */
    @SuppressWarnings("unchecked")
    public static <T> T clone(T source) {
        return (T) BeanUtil.copy(source, source.getClass());
    }

    /**
     * copy 对象属性到另一个对象，默认不使用Convert
     */
    public static <T> T copy(Object source, Class<T> clazz) {
        BaseBeanCopier copier = BaseBeanCopier.create(source.getClass(), clazz, false);
        T to = newInstance(clazz);
        copier.copy(source, to, null);
        return to;
    }

    /**
     * 拷贝对象
     */
    public static void copy(Object source, Object targetBean) {
        BaseBeanCopier copier = BaseBeanCopier.create(source.getClass(), targetBean.getClass(), false);
        copier.copy(source, targetBean, null);
    }

    public static <T> T copyProperties(Object source, Class<T> target) throws BeansException {
        T to = newInstance(target);
        BeanUtil.copyProperties(source, to);
        return to;
    }

    /**
     * 将对象装成map形式
     */
    @SuppressWarnings("unchecked")
    public static Map<String, Object> toMap(Object bean) {
        return BeanMap.create(bean);
    }

    /**
     * 将map 转为 bean
     */
    public static <T> T toBean(Map<String, Object> beanMap, Class<T> valueType) {
        T bean = BeanUtil.newInstance(valueType);
        BeanMap.create(bean).putAll(beanMap);
        return bean;
    }

    /**
     * 给一个Bean添加字段
     */
    public static Object generator(Object superBean, BeanProperty... props) {
        Class<?> superclass = superBean.getClass();
        Object genBean = generator(superclass, props);
        BeanUtil.copy(superBean, genBean);
        return genBean;
    }

    /**
     * 给一个class添加字段
     */
    public static Object generator(Class<?> superclass, BeanProperty... props) {
        BeanGenerator generator = new BeanGenerator();
        generator.setSuperclass(superclass);
        generator.setUseCache(true);
        for (BeanProperty prop : props) {
            generator.addProperty(prop.getName(), prop.getType());
        }
        return generator.create();
    }

    /**
     * 获取 Bean 的所有 get方法
     */
    public static PropertyDescriptor[] getBeanGetters(Class<?> type) {
        return getPropertiesHelper(type, true, false);
    }

    /**
     * 获取 Bean 的所有 set方法
     */
    public static PropertyDescriptor[] getBeanSetters(Class<?> type) {
        return getPropertiesHelper(type, false, true);
    }

    private static PropertyDescriptor[] getPropertiesHelper(Class<?> type, boolean read, boolean write) {
        try {
            PropertyDescriptor[] all = getPropertyDescriptors(type);
            if (read && write) return all;

            List<PropertyDescriptor> properties = new ArrayList<>(all.length);
            for (PropertyDescriptor pd : all) {
                if (read && pd.getReadMethod() != null) {
                    properties.add(pd);
                } else if (write && pd.getWriteMethod() != null) {
                    properties.add(pd);
                }
            }
            return properties.toArray(new PropertyDescriptor[0]);
        } catch (BeansException ex) {
            throw new CodeGenerationException(ex);
        }
    }


    /**
     * 绑定map里的值到 obj 属性上
     */
    public static void transMapToBean(Map<String, Object> map, Object obj) {
        try {
            PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(obj);
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                if (map.containsKey(key)) {
                    Object value = map.get(key);
                    Method setter = property.getWriteMethod();
                    setter.invoke(obj, value);
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    public static Map<String, Object> transBeanToMap(Object obj) {
        if (obj == null) return null;

        Map<String, Object> map = new HashMap<>();
        try {
            PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(obj);
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                // 过滤class属性
                if (!key.equals("class")) {
                    // 得到property对应的getter方法
                    Method getter = property.getReadMethod();
                    Object value = getter.invoke(obj);
                    map.put(key, value);
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return map;
    }

    private static PropertyDescriptor[] getPropertyDescriptors(Object obj) throws IntrospectionException {
        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        return beanInfo.getPropertyDescriptors();
    }

   /* public static void main(String[] args) {
        Map<String, Object> map = new HashMap<>();
        map.put("code", 1);
        map.put("msg", "xx");

        R r = new R();
        transMapToBean(map, r);
        System.out.println(r.getMsg());

        Map<String, Object> map1 = transBeanToMap(r);
        System.out.println(map1);
    }*/
}
